package cn.com.surker.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redisCache 工具类
 * 
 */
@Component
public class RedisUtils {

	@Autowired
	private RedisTemplate redisTemplate;

	static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);

	private static String byte2hex(byte[] buffer) {
		String h = "0x";

		for (byte aBuffer : buffer) {
			String temp = Integer.toHexString(aBuffer & 0xFF);
			if (temp.length() == 1) {
				temp = "0" + temp;
			}
			h = h + " " + temp;
		}

		return h;

	}

	/**
	 * 批量删除对应的value
	 *
	 * @param keys 缓存键
	 */
	public void remove(final String... keys) {
		for (String key : keys) {
			remove(key);
		}
	}

	/**
	 * 批量删除key
	 * 
	 * @param pattern 批量删除的键
	 */
	public void removePattern(final String pattern) {
		Set<Serializable> keys = redisTemplate.keys(pattern);
		if (keys.size() > 0)
			redisTemplate.delete(keys);
	}

	/**
	 * 删除对应的value
	 *
	 * @param key 缓存键
	 */
	public void remove(final String key) {
		if (exists(key)) {
			redisTemplate.delete(key);
		}
	}

	/**
	 * 判断缓存中是否有对应的value
	 *
	 * @param key 缓存键
	 * @return
	 */
	public boolean exists(final String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * 读取缓存
	 *
	 * @param key 缓存键
	 * @return
	 */
	public Object get(final String key) {
		Object result = null;
		ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
		result = operations.get(key);

		return result;
	}

	/**
	 * 写入缓存
	 *
	 * @param key 缓存键
	 * @param value 存入的值
	 * @return
	 */
	public boolean set(final String key, Object value) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 写入缓存
	 * 
	 * @param key 缓存键
	 * @param value 存入的值
	 * @param expireTime 多少秒后失效
	 * @return
	 */
	public boolean set(final String key, Object value, Long expireTime) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * WATCH 命令的作用， 就是将当前客户端和要监视的键在 watched_keys 中进行关联。
	 * 乐观锁工作机制：watch 命令会监视给定的每一个key，当exec时如果监视的任一个key自从调用watch后发生过变化，
	 * 则整个事务会回滚，不执行任何动作。注意watch的key是对整个连接有效的，事务也一样。如果连接断开，监视和事务都会被自动清除。
	 * 当然exec，discard，unwatch命令，及客户端连接关闭都会清除连接中的所有监视。还有，如果watch一个不稳定(有生命周期)的key并且此key自然过期，
	 * exec仍然会执行事务队列的指令。
	 * @param key
	 */
	public void watch(String key){
		redisTemplate.watch(key);
	}
	
	/**
	 * Redis MULTI命令标志着一个事务块的开始。后续将使用EXEC命令执行排队等待原子。
	 */
	public void multi(){
		redisTemplate.multi();
	}
	
	/**
	 * Redis EXEC命令执行所有先前排队在一个事务中的命令，并恢复连接状态正常。
	 * @return 返回数组，每个元素的应答都是在原子事务的每个命令。
	 */
	public List exec(){
		return  redisTemplate.exec();
	}
	
	
	
	/**
	 * 从指定的列表右边出队,添加到目的列表中
	 * 
	 * @param srckey 源列表
	 * @param dstkey 目的列表
	 * @return
	 */
	public Object rpopPush(String srckey, String dstkey) {
		return redisTemplate.opsForList().rightPopAndLeftPush(srckey, dstkey);
	}
 
	/**
	 * 从队列的左边取出一条数据
	 * 
	 * @param key
	 *            　列表名
	 * @return
	 */
	public Object leftPop(String key) {
		return redisTemplate.opsForList().leftPop(key);
	}
 
	/**
	 * 从队列的右边取出一条数据
	 * 
	 * @param key
	 *            列表名
	 * @return
	 */
	public Object rightPop(String key) {
		return redisTemplate.opsForList().rightPop(key);
	}
 
	/**
	 * 把一个值添加到对应列表中
	 * 
	 * @param key
	 *            列表名
	 * @param value
	 *            　数据
	 * @return
	 */
	public Long leftPush(String key, Object value) {
		return redisTemplate.opsForList().leftPush(key, value);
	}
	
	
	/**
	 * 获取list 的长度
	 * @param key
	 * @return
	 */
	public Long getListSize(String key){
		return redisTemplate.opsForList().size(key);
	}

	/**
	 * 存数据到列表中
	 */
	public Long rightPush(String key,String value){
		// return redisTemplate.opsForList().rightPush(key, value);
		// 检查 key 是否存在
		Boolean keyExists = redisTemplate.hasKey(key);
		// 如果 key 存在
		if (Boolean.TRUE.equals(keyExists)) {
			// 获取 key 对应的列表所有值
			List<String> list = redisTemplate.opsForList().range(key, 0, -1);
			// 检查列表中是否存在指定的 value
			if (list != null && list.contains(value)) {
				logger.info("key:" + key + ";存在Value:" + value);
				return 0L; // 返回 0 表示没有插入
			}
		}
		// 如果 key 不存在，或者列表中不存在 value，则插入新值
		return redisTemplate.opsForList().rightPush(key, value);
	}


	/**
	 * 取出列表数据(不删除)
	 * @param key
	 * @return
	 */
	public List<Object> listValues(String key) {
		ListOperations listOperations = redisTemplate.opsForList();
		List range = listOperations.range(key, 0, -1);
		return range;
	}

	/**
	 *
	 * 删除列表中数据为value的值
	 * @param key
	 * @param count
	 * @param value
	 * @return
	 */
	public Long removeList(String key, long count, String value) {
		Long remove = redisTemplate.opsForList().remove(key, count, value);
		return remove;
	}
	
	/** redis set 集合操作************************************************/
	
	/**
	 * 往set 集合中  添加值
	 * @param key
	 * @param value
	 * @return
	 */
	public Long add(String key,Object  value){
		return redisTemplate.opsForSet().add(key,value);
	}
	
	/**
	 * 删除set 集合中某值
	 * @param key
	 * @param value
	 * @return
	 */
	public Long remove(String key,Object...  value){
		return redisTemplate.opsForSet().remove(key,value);
	}
	/** redis set 集合操作************************************************/


	/**
	 * 消息队列发送消息
	 * @param channel
	 * @param message
	 */
	public  void sendMessage(String channel,Object message){
		redisTemplate.convertAndSend(channel, message);
	}


	/**
	 * 设置琐,如果返回成功,表示该数据没有被加锁;
	 * 反之,表示已经加锁,请等待
	 *
	 * @param lockKey
	 * @return
	 */
	public boolean acquireLock(String lockKey) {
		Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 120, TimeUnit.SECONDS);
		return success != null && success;
	}

	/**
	 * 设置琐,如果返回成功,表示该数据没有被加锁;
	 * 反之,表示已经加锁,请等待
	 *
	 * @param lockKey 加锁key
	 * @param timeout 驾驶时间,单位为秒
	 * @return
	 */
	public boolean acquireLock(String lockKey, long timeout) {
		Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", timeout, TimeUnit.SECONDS);
		return success != null && success;
	}

	/**
	 * 是否存在琐
	 *
	 * @param lockKey
	 * @return
	 */
	public boolean hasLock(String lockKey) {
		Object o = redisTemplate.opsForValue().get(lockKey);
		if ("locked".equals(String.valueOf(o))) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 和acquireLock配对使用,用于解锁
	 *
	 * @param lockKey
	 */
	public void releaseLock(String lockKey) {
		redisTemplate.delete(lockKey);
	}
}
